﻿using System;
using System.Collections.Generic;
using EntitiesAndGameState;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Services;
using Utilities;

namespace WAG
{

    /// <summary>
    /// EffectsManager gestisce i sistemi particellari necessari nel gioco e si occupa di passare loro
    /// le informazioni aggiornate sulla camera (che ottiene in quanto CameraConsumer) e di aggiornare 
    /// le animazioni per le texture con tanti fotogrammi tipo le esplosioni).
    /// </summary>
    public class EffectsManager : CameraConsumer
    {

        //public class LightningEffect
        //{
        //    //private ArrayList<ThunderBoltSegmentation> segments;
        //    private bool projectileActive;
        //    public Vector3 endPoint;
        //    public Vector3 startPoint;
        //    public Projectile projectile;
        //    public float duration;
        //    public float slow_motion;

        //    public float scale;

        //    public float size;
        //    private JigLibX.Physics.Body shooted_body;
        //    private Ship shooter;

        //    public LightningEffect() { }

        //    public LightningEffect(Projectile projectile, float duration)
        //    {
        //        Init(projectile, duration);
        //    }

        //    public void Init(Projectile projectile, float duration)
        //    {
        //        this.projectileActive = true;
        //        this.projectile = projectile;
        //        this.duration = duration;
        //        this.size = 20.0f;//projectile.weapon_config.Damage * 10;

        //        this.shooter = projectile.shooter;
        //    }


        //    public void Update(GameTime gameTime)
        //    {
        //        if (!shooter.OwnValue.IsActive)
        //        {
        //            duration = 0.0f;
        //            return;
        //        }

        //        startPoint = shooter.geometry.Position;

        //        if (projectileActive)
        //        {
        //            endPoint = projectile.geometry.Position;
        //        }
        //        else
        //        {
        //            if (shooted_body == null)
        //            {
        //                duration = 0.0f;
        //                return;
        //            }
        //            if (!
        //                ((shooted_body.ExternalData is Ship && ((Ship)shooted_body.ExternalData).OwnValue.IsActive)
        //                || (shooted_body.ExternalData is Asteroid && ((Asteroid)shooted_body.ExternalData).OwnValue.IsActive))
        //                )
        //            {
        //                duration = 0.0f;
        //                return;
        //            }
        //            endPoint = shooted_body.Position;
        //        }

        //        if (projectileActive && !projectile.OwnValue.IsActive)
        //        {
        //            shooted_body = projectile.shot_body;
        //            projectileActive = false;
        //            if (shooted_body == null)
        //            {
        //                duration = 0.0f;
        //                return;
        //            }
        //        }

        //        duration -= (float)gameTime.ElapsedGameTime.TotalSeconds * slow_motion;
        //    }

        //    public float rotation
        //    {
        //        get
        //        {
        //            return -(float)(Math.Atan2((float)(endPoint.X - startPoint.X), (float)(endPoint.Z - startPoint.Z)));
        //        }
        //    }
        //}

        //public class LaserLineEffect
        //{
        //    public bool projectileActive;
        //    public Vector3 endPoint;
        //    public Vector3 startPoint;
        //    public Projectile projectile;
        //    public float duration;
        //    public float slow_motion;

        //    private JigLibX.Physics.Body target_body;
        //    private Ship shooter;

        //    private bool atarget;
        //    private float dist;

        //    public float size;

        //    public LaserLineEffect() { }

        //    public LaserLineEffect(Projectile projectile, float duration)
        //    {
        //        Init(projectile, duration);
        //    }

        //    public void Init(Projectile projectile, float duration)
        //    {
        //        this.projectile = projectile;
        //        this.duration = duration;
        //        this.projectileActive = true;
        //        this.atarget = false;
        //        this.shooter = projectile.shooter;
        //        this.startPoint = shooter.geometry.Position;
        //        this.endPoint = startPoint;
        //        this.target_body = projectile.target_geometry;
        //        this.size = 20f;
        //    }

        //    public void Update(GameTime gameTime)
        //    {
        //        if (!shooter.OwnValue.IsActive ||
        //            !((target_body.ExternalData is Ship && ((Ship)target_body.ExternalData).OwnValue.IsActive)
        //                || (target_body.ExternalData is Asteroid && ((Asteroid)target_body.ExternalData).OwnValue.IsActive))
        //            )
        //        {
        //            duration = 0.0f;
        //            return;
        //        }

        //        startPoint = shooter.geometry.Position;

        //        if (!projectileActive)
        //        {
        //            if (atarget)
        //                endPoint = target_body.Position;
        //            else
        //                endPoint = Vector3.Normalize(target_body.Position - shooter.geometry.Position) * dist + shooter.geometry.Position;
        //            duration -= (float)gameTime.ElapsedGameTime.TotalSeconds * slow_motion;
        //        }
        //        else
        //            dist = Vector3.Distance(projectile.geometry.Position, shooter.geometry.Position);

        //        if (projectileActive && !projectile.OwnValue.IsActive)
        //        {
        //            atarget = projectile.target_geometry == projectile.shot_body;
        //            projectileActive = false;
        //        }


        //    }

        //    public float rotation
        //    {
        //        get
        //        {
        //            return -(float)(Math.Atan2((float)(endPoint.X - startPoint.X), (float)(endPoint.Z - startPoint.Z)));
        //        }
        //    }
        //}

        //public class MissileEffect
        //{

        //    public Projectile projectile;
        //    public bool projectileActive;

        //    public MissileEffect() { }

        //    public MissileEffect(Projectile projectile)
        //    {
        //        Init(projectile);
        //    }

        //    public void Init(Projectile projectile)
        //    {
        //        this.projectile = projectile;
        //        this.projectileActive = true;
        //    }

        //    public void Update(GameTime gameTime)
        //    {
        //        projectileActive = projectile.OwnValue.IsActive;
        //    }

        //    public float rotation
        //    {
        //        get
        //        {
        //            var p = projectile.geometry.Position;
        //            var d = projectile.geometry.Velocity + p;
        //            return -(float)(Math.Atan2((float)(d.X - p.X), (float)(d.Z - p.Z)));
        //        }
        //    }
        //}

        public class Explosion
        {
            public Vector3 position;
            public float size;
            public float age;
            public void Update(GameTime gameTime) { age += (float)gameTime.ElapsedGameTime.TotalSeconds; }
        }

        IStateProvider<BaseLevelStats> state_provider;

        //public class EnemyWarp
        //{
        //    public float warping_out_timer = 0.5f;

        //    public WarpParticle particle;
        //    Ship ship;

        //    public EnemyWarp(EffectsManager em, Ship ship)
        //    {            
        //        this.ship = ship;
        //        particle = em.AddWarparticle(ship.geometry.Position, 0.0f, Color.White);            
        //    }

        //    public void Update(float dt)
        //    {
        //        warping_out_timer -= dt;
        //        var warp_ratio = 1.0f - warping_out_timer / PlayerShip.WARP_TRANSITION_TIME;
        //        if (warp_ratio > 0.75f)
        //        {
        //            warp_ratio = (warp_ratio - 0.75f) * 4.0f;
        //            particle.Position = ship.geometry.Position +
        //                (1.0f - warp_ratio) * ship.geometry.Orientation.Forward * ship.geometry.bounding_sphere().Radius * 0.75f;
        //            particle.Size = MathHelper.SmoothStep(0.02f, 0.01f, warp_ratio) * 0.9f;
        //        }
        //        else
        //        {
        //            warp_ratio *= 4.0f / 3.0f;
        //            particle.Position = ship.geometry.Position +
        //                warp_ratio * ship.geometry.Orientation.Forward * ship.geometry.bounding_sphere().Radius * 0.75f;
        //            particle.Size = MathHelper.SmoothStep(0.05f, 0.02f, warp_ratio) * 0.9f;
        //        }
        //    }

        //    public void Dispose()
        //    { 

        //    }
        //}
        public class EnemyX
        {
            public float x_time_animation = PlayerShip.X_TRANSITION_TIME;

            public XParticle particle;
            Vector3 shipPosition;
            public EnemyX(EffectsManager em, Vector3 ship_pos)
            {
                particle = em.AddXparticle(Vector3.Zero, 0.0f, Color.White);
                shipPosition = ship_pos;
            }

            public void Update(float dt)
            {
                x_time_animation -= dt;
                var x_ratio = 1 - MathHelper.Clamp(x_time_animation / PlayerShip.X_TRANSITION_TIME, 0, 1);
                particle.Size = MathHelper.SmoothStep(0.9f, 0.0f, x_ratio);
                particle.Position = Vector3.SmoothStep(Vector3.Zero, shipPosition, x_ratio);
            }

            public void Dispose()
            {

            }
        }
        public class PlasmaParticle : Particle { }
        public class EngineParticle : Particle { }
        public class SmokeParticle : Particle { }
        public class WarpParticle : Particle { }
        public class XParticle : Particle { }
        public class MachineGunParticle : Particle { }
        public class MuzzleFlashParticle : Particle { }

        //public class LaserShot
        //{
        //    public Particle LaserLine;
        //    public Particle ShooterGlobus, TargetGlobus;
        //    public Vector3 ShooterPosition;
        //    public Vector3 TargetPosition;
        //    public Color LaserColor;
        //    // identificatore da associare allo shot, in modo da poterlo recuperare dalla lista dei lasershot, univocamente
        //    public float MAXLIFE;
        //    public float life;
        //    // rotazione da applicare alla texture, da correggere in caso di texture NON orientata verso destra ( X positiva)
        //    public float Angle
        //    {
        //        get
        //        {
        //            return (float)Math.Atan2(
        //                (float)(TargetPosition.Z - ShooterPosition.Z),
        //                (float)(TargetPosition.X - ShooterPosition.X));
        //        }
        //    }
        //    // scalatura da applicare alla texture, pari alla distanza tra shooter e target
        //    public Vector2 Scale
        //    {
        //        get
        //        {
        //            return new Vector2(0.1f, Vector3.Distance(ShooterPosition, TargetPosition) * 0.1f);
        //        }
        //    }

        //    public Vector3 Centroid
        //    {
        //        get
        //        {
        //            return new Vector3((ShooterPosition.X + TargetPosition.X) / 2,
        //                    0.0f, (ShooterPosition.Z + TargetPosition.Z) / 2);
        //        }
        //    }
        //}

        //public class VanishingText
        //{
        //    public Vector3 position;
        //    public string text;
        //    public float time_left;
        //    public void Update(GameTime gameTime) { time_left -= (float)gameTime.ElapsedGameTime.TotalSeconds; }
        //}

        //ParticleSystem<SmokeParticle> smoke;
        //ParticleSystem<PlasmaParticle> plasma;
        //ParticleSystem<PlasmaParticle> chainreaction;
        //ParticleSystem<MachineGunParticle> machine_gun;
        //ParticleSystem<MuzzleFlashParticle> muzzle_flash;
        ParticleSystem<EngineParticle> plasma_engine;
        //ParticleSystem<WarpParticle> warp;
        ParticleSystem<XParticle> x;

        public EffectsManager(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            game.Services.AddService(typeof(EffectsManager), this);
            //smoke = new ParticleSystem<SmokeParticle>(Game, @"Explosion\smoke");
            //plasma = new ParticleSystem<PlasmaParticle>(Game, @"Explosion\plasma");
            //chainreaction = new ParticleSystem<PlasmaParticle>(Game, @"Explosion\chainreaction");
            //machine_gun = new ParticleSystem<MachineGunParticle>(Game, @"Explosion\mg_shot");
            //muzzle_flash = new ParticleSystem<MuzzleFlashParticle>(Game, @"Explosion\Muzzle_Flash_Side", BlendState.AlphaBlend, 0.025f);

            //  smoke = fumo troppo trasparente, blue_glow2 = chiazzette blu no, chainreaction chiazzette verdi no, exp3_1 = fuoco fichissimo
            // explosion = gran volume di fuoco, fire = fuoco sporco MIGLIORE
            //var paths = new string[] { "fire", "explosion", "exp3_1", "smoke" };
            //for (int i = 0; i < paths.Length; i++)
            //{
            //    paths[i] = @"Explosion\" + paths[i];
            //}
            plasma_engine = new ParticleSystem<EngineParticle>(Game, @"Explosion\fire");
            //plasma_engine = new ParticleSystem<EngineParticle>(Game, paths);
            //warp = new ParticleSystem<WarpParticle>(Game, @"CachedEffects\warp");
            x = new ParticleSystem<XParticle>(Game, @"CachedEffects\x");

            vanishing_text_font = game.Content.Load<SpriteFont>(@"Fonts\Arial14Ptx");
            //smoke.DrawOrder = (int)ComponentOrders.EFFECTS;
            //plasma.DrawOrder = (int)ComponentOrders.EFFECTS;
            //chainreaction.DrawOrder = (int)ComponentOrders.EFFECTS;
            //machine_gun.DrawOrder = (int)ComponentOrders.EFFECTS;
            //muzzle_flash.DrawOrder = (int)ComponentOrders.EFFECTS;
            plasma_engine.DrawOrder = (int)ComponentOrders.EFFECTS;
            x.DrawOrder = (int)ComponentOrders.EFFECTS;
            //warp.DrawOrder = (int)ComponentOrders.EFFECTS;


            //Game.Components.Add(smoke);
            //Game.Components.Add(chainreaction);
            //Game.Components.Add(plasma);
            //Game.Components.Add(machine_gun);
            //Game.Components.Add(muzzle_flash);
            Game.Components.Add(plasma_engine);
            //Game.Components.Add(warp);
            Game.Components.Add(x);


            this.UpdateOrder = (int)ComponentOrders.EFFECTS;
            this.DrawOrder = (int)ComponentOrders.EFFECTS;
        }

        protected override void Dispose(bool disposing)
        {
            Game.Services.RemoveService(typeof(EffectsManager));
            base.Dispose(disposing);
        }

        SpriteFont vanishing_text_font;
        AnimationDescription explosion_animation;
        ShockWaveDescription shockwave_animation;
        //SlideAnimationDescription thunderbolt_animation;
        //SlideAnimationDescription laserline_animation;
        //MissileDescription missile_description, nuke_description;
        struct AnimationDescription
        {
            public Texture2D texture;
            public float frame_length;
            public int frame_width;
            public int frame_height;
            public bool TooOld(float age)
            {
                var frames_per_row = texture.Width / frame_width;
                var frames_per_column = texture.Height / frame_height;
                return (int)(age / frame_length) > frames_per_row * frames_per_column;
            }
            public Rectangle GetFrameBounds(float age)
            {
                var frames_per_row = texture.Width / frame_width;
                int i = (int)(age / frame_length);
                var rect = new Rectangle((i % frames_per_row) * frame_width,
                                     (i / frames_per_row) * frame_height,
                                     frame_width, frame_height);
                return rect;
            }
        }

        struct ShockWaveDescription
        {
            public Texture2D texture;
            public bool TooOld(float range, float radius)
            {
                return radius >= range;
            }
        }
        //struct MissileDescription
        //{
        //    public Texture2D texture;
        //    public float scale;
        //}
        //struct SlideAnimationDescription
        //{
        //    public Texture2D texture;
        //    public float slideSpeed;
        //    public float textureScale;

        //    public Rectangle GetFrameBounds(float age, float length)
        //    {
        //        length *= textureScale;
        //        //var start = (texture.Height-length)-(age * slideSpeed) % (texture.Height - length);
        //        var start = (age * slideSpeed) % (texture.Height - length);
        //        var rect = new Rectangle(0, (int)start, texture.Width, (int)length);
        //        return rect;
        //    }
        //}


        ArrayList<Explosion> explosions = new ArrayList<Explosion>(100);
        Stack<Explosion> free_explosions = new Stack<Explosion>(100);

        ArrayList<ShockWave> shockwaves = new ArrayList<ShockWave>(15);
        Stack<ShockWave> free_shockwaves = new Stack<ShockWave>(15);

        //ArrayList<VanishingText> vanishing_texts = new ArrayList<VanishingText>(100);
        //Stack<VanishingText> free_vanishing_texts = new Stack<VanishingText>(100);
        //List<VanishingText> vanishing_texts_to_add = new ArrayList<VanishingText>(100); 

        //ArrayList<LightningEffect> thunderbolts = new ArrayList<LightningEffect>(20);
        //Stack<LightningEffect> free_thunderbolts = new Stack<LightningEffect>(20);
        //List<LightningEffect> thunderbolts_to_add = new ArrayList<LightningEffect>(20);

        //ArrayList<LaserLineEffect> laserlines = new ArrayList<LaserLineEffect>(40);
        //Stack<LaserLineEffect> free_laserlines = new Stack<LaserLineEffect>(40);
        //List<LaserLineEffect> laserlines_to_add = new ArrayList<LaserLineEffect>(40);

        //ArrayList<MissileEffect> missiles = new ArrayList<MissileEffect>(40);
        //Stack<MissileEffect> free_missiles = new Stack<MissileEffect>(40);

        //ArrayList<MissileEffect> nukes = new ArrayList<MissileEffect>(40);
        //Stack<MissileEffect> free_nukes = new Stack<MissileEffect>(40);

        //public ArrayList<LaserShot> Lasers = new ArrayList<LaserShot>(10);
        // Texture2D LasersTexture;

        SpriteBatch spriteBatch;

        public override void Initialize()
        {
            var camera_producer = Game.Services.GetService(typeof(CameraProvider)) as CameraProvider;
            camera_producer.AddCameraConsumer(this);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            state_provider = Game.Services.GetService(typeof(IStateProvider<BaseLevelStats>))
                as IStateProvider<BaseLevelStats>;

            explosion_animation = new AnimationDescription()
            {
                texture = Game.Content.Load<Texture2D>(@"CachedEffects\explosion"),
                frame_height = 100,
                frame_width = 100,
                frame_length = 1.0f / 20.0f
            };
            shockwave_animation = new ShockWaveDescription()
            {
                texture = Game.Content.Load<Texture2D>(@"Explosion\shockwave")
            };

            //thunderbolt_animation = new SlideAnimationDescription()
            //{
            //    texture = Game.Content.Load<Texture2D>(@"Projectiles\thunderbolt"),
            //    slideSpeed = 300f,
            //    textureScale = 1
            //};

            //laserline_animation = new SlideAnimationDescription()
            //{
            //    texture = Game.Content.Load<Texture2D>(@"Projectiles\laser"),
            //    slideSpeed = 0f,
            //    textureScale = 1f
            //};

            //missile_description = new MissileDescription()
            //{
            //    texture = Game.Content.Load<Texture2D>(@"Projectiles\missile_texture"),
            //    scale = 5f
            //};

            //nuke_description = new MissileDescription()
            //{
            //    texture = Game.Content.Load<Texture2D>(@"Projectiles\missile_texture"),
            //    scale = 4f
            //};

            //LasersTexture = Game.Content.Load<Texture2D>(@"Explosion\blue_line");
        }


        public struct genericPartilceInfo
        {
            public float Age;
            public float AgingSpeed;
            public float MaxAge;
            public float Size;
            public Vector3 Position;
            public Vector3 Velocity;
            public bool IsActive;
            public Color Color;
            public Vector3 Gravity;
            public float Rotation;
            public Vector2 Scale;
        }
        public static genericPartilceInfo gpi;
        public static Action<Particle> init_gp = new Action<Particle>(p =>
        {
            p.Age = gpi.Age;
            p.AgingSpeed = gpi.AgingSpeed;
            p.MaxAge = gpi.MaxAge;
            p.Size = gpi.Size;
            p.Position = gpi.Position;
            p.Velocity = gpi.Velocity;
            p.IsActive = gpi.IsActive;
            p.Color = gpi.Color;
            p.Gravity = gpi.Gravity;
            p.Rotation = gpi.Rotation;
            p.Scale = gpi.Scale;
        });


        //public SmokeParticle AddSmokeParticle(Vector3 position, Vector3 velocity, float size, Color color)
        //{
        //  gpi = new genericPartilceInfo()
        //  {
        //    Age = 1.0f,
        //    AgingSpeed = 1.0f,
        //    MaxAge = 1.0f,
        //    Size = size / 20.0f,
        //    Position = position,
        //    Velocity = velocity,
        //    IsActive = true,
        //    Color = color,
        //    Gravity = Vector3.Zero,
        //    Rotation = 0.0f,
        //    Scale = new Vector2(1.0f, 1.0f)
        //  };
        //  return smoke.AddParticle(init_gp);
        //}
        //public PlasmaParticle AddPlasmaParticle(Vector3 position, Vector3 velocity, float size, Color color)
        //{
        //    gpi = new genericPartilceInfo()
        //    {
        //        Age = 0.2f,
        //        AgingSpeed = 0.0f,
        //        MaxAge = 0.2f,
        //        Size = size / 4.0f,
        //        Position = position,
        //        Velocity = velocity,
        //        IsActive = true,
        //        Color = color,
        //        Gravity = Vector3.Zero,
        //        Rotation = 0.0f,
        //        Scale = new Vector2(1.0f, 1.0f)
        //    };
        //    return plasma.AddParticle(init_gp);
        //}
        //public PlasmaParticle AddChainReactionParticle(Vector3 position, Vector3 velocity, float size, Color color)
        //{
        //    gpi = new genericPartilceInfo()
        //    {
        //        Age = 0.2f,
        //        AgingSpeed = 0.0f,
        //        MaxAge = 0.2f,
        //        Size = size / 4.0f,
        //        Position = position,
        //        Velocity = velocity,
        //        IsActive = true,
        //        Color = color,
        //        Gravity = Vector3.Zero,
        //        Rotation = 0.0f,
        //        Scale = new Vector2(1.0f, 1.0f)
        //    };
        //    return chainreaction.AddParticle(init_gp);
        //}
        //public MachineGunParticle AddMachineGunParticle(Vector3 position, Vector3 velocity, float size, Color color, float rotation)
        //{
        //    gpi = new genericPartilceInfo()
        //    {
        //        Age = 0.2f,
        //        AgingSpeed = 0.0f,
        //        MaxAge = 0.2f,
        //        Size = size / 4.0f,
        //        Position = position,
        //        Velocity = velocity,
        //        IsActive = true,
        //        Color = color,
        //        Gravity = Vector3.Zero,
        //        Rotation = rotation,
        //        Scale = new Vector2(1.0f, 1.0f)
        //    };
        //    return machine_gun.AddParticle(init_gp);
        //}

        public EngineParticle AddEngineParticle(Vector3 position, Vector3 velocity, float size, Color color)
        {
            gpi = new genericPartilceInfo()
            {
                Age = 0.2f,
                AgingSpeed = 1.0f,
                MaxAge = 0.2f,
                Size = size / 10.0f,
                Position = position,
                Velocity = velocity,
                IsActive = true,
                Color = color,
                Gravity = Vector3.Zero,
                Rotation = 0.0f,
                Scale = new Vector2(1.0f, 1.0f)
            };
            return plasma_engine.AddParticle(init_gp);
        }

        //public WarpParticle AddWarparticle(Vector3 position, float size, Color color)
        //{
        //    gpi = new genericPartilceInfo()
        //   {
        //       Age = 0.8f,
        //       AgingSpeed = 0.0f,
        //       MaxAge = 0.8f,
        //       Size = size / 4.0f,
        //       Position = position,
        //       Velocity = Vector3.Zero,
        //       IsActive = true,
        //       Color = color,
        //       Gravity = Vector3.Zero,
        //       Rotation = 0.0f,
        //       Scale = new Vector2(1.0f, 1.0f)
        //   };
        //    return warp.AddParticle(init_gp);
        //}
        public XParticle AddXparticle(Vector3 position, float size, Color color)
        {
            gpi = new genericPartilceInfo()
            {
                Age = 0.8f,
                AgingSpeed = 0.0f,
                MaxAge = 0.8f,
                Size = size / 4.0f,
                Position = position,
                Velocity = Vector3.Zero,
                IsActive = true,
                Color = color,
                Gravity = Vector3.Zero,
                Rotation = 0.0f,
                Scale = new Vector2(1.0f, 1.0f)
            };
            return x.AddParticle(init_gp);
        }


        // max life sarà dato da fire_rate + number_of_shots
        //public void AddLaserShot(Vector3 Shooter_Position, Vector3 Target_Position, float MaxLife, Color color)
        //{
        //    Lasers.Add(
        //        new LaserShot()
        //        {
        //            ShooterPosition = Shooter_Position,
        //            TargetPosition = Target_Position,
        //            MAXLIFE = MaxLife,
        //            life = 0.0f,
        //            LaserColor = color

        //        });
        //}

        //public LightningEffect AddThunderbolt(Action<LightningEffect> init)
        //{
        //    if (free_thunderbolts.Count <= 0) free_thunderbolts.Push(new LightningEffect());
        //    var lig = free_thunderbolts.Pop();
        //    init(lig);
        //    thunderbolts.Add(lig);
        //    return lig;
        //}

        //public LaserLineEffect AddLaserLine(Action<LaserLineEffect> init)
        //{
        //    if (free_laserlines.Count <= 0) free_laserlines.Push(new LaserLineEffect());
        //    var lig = free_laserlines.Pop();
        //    init(lig);
        //    laserlines.Add(lig);
        //    return lig;
        //}

        //public MissileEffect AddMissile(Action<MissileEffect> init)
        //{
        //    if (free_missiles.Count <= 0) free_missiles.Push(new MissileEffect());
        //    var m = free_missiles.Pop();
        //    init(m);
        //    missiles.Add(m);
        //    return m;
        //}
        //public MissileEffect AddNuke(Action<MissileEffect> init)
        //{
        //    if (free_nukes.Count <= 0) free_nukes.Push(new MissileEffect());
        //    var n = free_nukes.Pop();
        //    init(n);
        //    nukes.Add(n);
        //    return n;
        //}
        //public MuzzleFlashParticle AddMuzzleFlashParticle(Vector3 position, float size, Color color, float rotation)
        //{
        //    gpi = new genericPartilceInfo
        //    {
        //        Age = 0.5f,
        //        AgingSpeed = 1.0f,
        //        MaxAge = 0.51f,
        //        Size = size / 4.0f,
        //        Position = position,
        //        Velocity = Vector3.Zero,
        //        IsActive = true,
        //        Color = color,
        //        Gravity = Vector3.Zero,
        //        Rotation = rotation,
        //        Scale = new Vector2(1.0f, 1.0f)
        //    };
        //    return muzzle_flash.AddParticle(init_gp);
        //}

        public void AddExplosion(Action<Explosion> init)
        {
            if (free_explosions.Count <= 0) free_explosions.Push(new Explosion());
            var exp = free_explosions.Pop();

            init(exp);

            explosions.Add(exp);
        }

        //public void AddVanishingText(Action<VanishingText> init)
        //{
        //    if (free_vanishing_texts.Count <= 0) free_vanishing_texts.Push(new VanishingText());
        //    var exp = free_vanishing_texts.Pop();

        //    init(exp);

        //    vanishing_texts.Add(exp);
        //}
        public void AddShockWave(Action<ShockWave> init)
        {
            if (free_shockwaves.Count <= 0) free_shockwaves.Push(new ShockWave());
            var exp = free_shockwaves.Pop();

            init(exp);

            shockwaves.Add(exp);
            //shockwaves_to_add.Add(shockwave);
        }

        public class ShockWave
        {
            public Vector3 position;
            public Color initialColor;
            public Color finalColor;
            public float range;
            public float radius;
            public float speed;
            public float status { get { return Math.Min(1.0f, radius / range); } }
            public void Update(GameTime gameTime, bool enabled)
            {
                if (enabled)
                    radius += speed * (float)gameTime.ElapsedGameTime.TotalSeconds ;// *speed;
            }
        }

        public override void Update(GameTime gameTime)
        {
            var dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //explosions.AddRange(explosions_to_add);
            //explosions_to_add.Clear();        
            //List<Explosion> explosions_to_remove = new ArrayList<Explosion>();
            //for_each (var e in explosions)
            //{
            //    e.Update(gameTime);
            //    if (explosion_animation.TooOld(e.age))
            //        explosions_to_remove.Add(e);
            //}
            //for_each (var e in explosions_to_remove) explosions.Remove(e);

            for (int i = 0; i < explosions.Count; i++)
            {
                var exp = explosions[i];
                if (explosion_animation.TooOld(exp.age))
                {
                    explosions.Remove(exp);
                    free_explosions.Push(exp);
                    i--;
                }
                else
                {
                    exp.Update(gameTime);
                }
            }
            //for_each (var e in explosions_to_remove) explosions.Remove(e);


            for (int i = 0; i < shockwaves.Count; i++)
            {
                var s = shockwaves[i];
                if (shockwave_animation.TooOld(s.range, s.radius))
                {
                    shockwaves.Remove(s);
                    free_shockwaves.Push(s);
                    i--;
                }
                else
                {
                    s.Update(gameTime, state_provider.Enabled);
                }
            }
            //shockwaves.AddRange(shockwaves_to_add);
            //shockwaves_to_add.Clear();
            //List<ShockWave> shockwaves_to_remove = new ArrayList<ShockWave>();
            //for_each (var s in shockwaves)
            //{
            //    s.Update(gameTime);
            //    if (shockwave_animation.TooOld(s.range, s.radius))
            //        shockwaves_to_remove.Add(s);
            //}
            //for_each (var s in shockwaves_to_remove) shockwaves.Remove(s);

            //thunderbolts.AddRange(thunderbolts_to_add);
            //thunderbolts_to_add.Clear();
            //List<LightningEffect> thunderbolts_to_remove = new ArrayList<LightningEffect>();
            //for_each (var e in thunderbolts)
            //{
            //    e.Update(gameTime);
            //    if (e.duration <= 0.0f)
            //        thunderbolts_to_remove.Add(e);
            //}
            //for_each (var e in thunderbolts_to_remove) thunderbolts.Remove(e);
            //for(int i=0; i<thunderbolts.Count; i++)
            //{
            //    var e = thunderbolts[i];            
            //    if (e.duration <= 0.0f)
            //    {
            //        thunderbolts.Remove(e);
            //        free_thunderbolts.Push(e);
            //        i--;
            //    }
            //    else
            //        e.Update(gameTime);
            //}

            //laserlines.AddRange(laserlines_to_add);
            //laserlines_to_add.Clear();
            //List<LaserLineEffect> laserlines_to_remove = new ArrayList<LaserLineEffect>();
            //for_each (var e in laserlines)
            //{
            //    e.Update(gameTime);
            //    if (e.duration <= 0.0f)
            //        laserlines_to_remove.Add(e);
            //}
            //for_each (var e in laserlines_to_remove) laserlines.Remove(e);
            //for (int i = 0; i < laserlines.Count; i++)
            //{
            //    var e = laserlines[i];
            //    if (e.duration <= 0.0f)
            //    {
            //        laserlines.Remove(e);
            //        free_laserlines.Push(e);
            //        i--;
            //    }
            //    else
            //        e.Update(gameTime);
            //}

            //for (int i = 0; i < missiles.Count; i++)
            //{
            //    var e = missiles[i];
            //    //if (e.duration <= 0.0f)
            //    if(!e.projectileActive)
            //    {
            //        missiles.Remove(e);
            //        free_missiles.Push(e);
            //        i--;
            //    }
            //    else
            //        e.Update(gameTime);
            //}
            //for (int i = 0; i < nukes.Count; i++)
            //{
            //    var e = nukes[i];
            //    //if (e.duration <= 0.0f)
            //    if (!e.projectileActive)
            //    {
            //        nukes.Remove(e);
            //        free_nukes.Push(e);
            //        i--;
            //    }
            //    else
            //        e.Update(gameTime);
            //}
            //vanishing_texts.AddRange(vanishing_texts_to_add);
            //vanishing_texts_to_add.Clear();
            //List<VanishingText> vanishing_texts_to_remove = new ArrayList<VanishingText>();
            //for_each (var e in vanishing_texts)
            //{
            //    e.Update(gameTime);
            //    if (e.time_left <= 0.0f)
            //        vanishing_texts_to_remove.Add(e);
            //}
            //for_each (var e in vanishing_texts_to_remove) vanishing_texts.Remove(e);

            //for (int i = 0; i < vanishing_texts.Count; i++)
            //{
            //    var e = vanishing_texts[i];
            //    if (e.time_left <= 0.0f)
            //    {
            //        vanishing_texts.Remove(e);
            //        free_vanishing_texts.Push(e);
            //        i--;
            //    }
            //    else
            //        e.Update(gameTime);
            //}

            //chainreaction.SetCamera(view, projection, max_depth);
            //plasma.SetCamera(view, projection, max_depth);
            //smoke.SetCamera(view, projection, max_depth);
            plasma_engine.SetCamera(view, projection, max_depth);
            //warp.SetCamera(view, projection, max_depth);
            x.SetCamera(view, projection, max_depth);
            //for (int i = 0; i < Lasers.Count; i++)
            //{

            //    Lasers[i].life += dt;
            //    if (Lasers[i].life > Lasers[i].MAXLIFE)
            //    {
            //        Lasers.RemoveAt(i);
            //        i--;
            //    }
            //}
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
            for (int i = 0; i < shockwaves.Count; i++)
            {
                var s = shockwaves[i];
                var sp = GraphicsDevice.Viewport.Project(s.position, projection, view, Matrix.Identity);
                var color = Color.Lerp(s.initialColor, s.finalColor, s.status);
                var alpha = 1;
                var r = s.radius * 30;
                spriteBatch.Draw(
                    shockwave_animation.texture,
                    new Rectangle((int)(sp.X - r), (int)(sp.Y - r), (int)(r * 2), (int)(r * 2)),
                    color * alpha
                );
            }
            spriteBatch.End();

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive);
            //for_each (var e in explosions)
            for (int i = 0; i < explosions.Count; i++)
            {
                var e = explosions[i];
                var sp = GraphicsDevice.Viewport.Project(e.position, projection, view, Matrix.Identity);
                var rect = explosion_animation.GetFrameBounds(e.age);
                spriteBatch.Draw(explosion_animation.texture, new Vector2(sp.X, sp.Y), rect, Color.White, 0.0f,
                                  new Vector2(rect.Width, rect.Height) * 0.5f, e.size * max_depth / (Vector3.Distance(e.position, EyePosition)),
                                  SpriteEffects.None, 0.0f);
            }
            spriteBatch.End();

            //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive);
            //for_each (var e in thunderbolts)
            //for (int i = 0; i < thunderbolts.Count; i++)
            //{ 
            //    var e = thunderbolts[i];

            //    var zoomScale = 1 / Vector3.Distance(EyePosition, e.startPoint);

            //    var sp = GraphicsDevice.Viewport.Project(e.startPoint, projection, view, Matrix.Identity);
            //    var dp = GraphicsDevice.Viewport.Project(e.endPoint, projection, view, Matrix.Identity);

            //    //var dir = (e.destination-e.source);
            //    //ir.Normalize();
            //    //var ang = (float)GetSignedAngleBetween2DVectors(Vector3.Backward,dir,Vector3.Right);
            //    //var ang = (float)((Math.Atan2((float)(dp.Z - sp.Z), (float)(dp.X - sp.X)) + 3 * Math.PI / 2) % (Math.PI * 2));

            //    //(float)Math.Acos(Vector3.Dot(Vector3.Backward, dir));
            //    var rect = thunderbolt_animation.GetFrameBounds(e.duration, (float)Math.Abs(Vector3.Distance(sp, dp) * (1 / (e.size)) * (1/zoomScale)));
            //    spriteBatch.Draw(thunderbolt_animation.texture, new Vector2(sp.X, sp.Y), rect, Color.White,
            //                    e.rotation, new Vector2(rect.Width, 0) * 0.5f, 
            //                    thunderbolt_animation.textureScale * e.size * zoomScale, SpriteEffects.None, 0.0f);
            //}
            //spriteBatch.End();


            //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive);
            ////for_each (var e in laserlines)
            //for (int i = 0; i < laserlines.Count;i++ )
            //{
            //    var e = laserlines[i];
            //    var zoomScale = 1 / Vector3.Distance(EyePosition, e.startPoint);

            //    if (e.projectileActive)
            //        continue;
            //    var sp = GraphicsDevice.Viewport.Project(e.startPoint, projection, view, Matrix.Identity);
            //    var dp = GraphicsDevice.Viewport.Project(e.endPoint, projection, view, Matrix.Identity);

            //    //var dir = (e.destination-e.source);
            //    //ir.Normalize();
            //    //var ang = (float)GetSignedAngleBetween2DVectors(Vector3.Backward,dir,Vector3.Right);
            //    //var ang = (float)((Math.Atan2((float)(dp.Z - sp.Z), (float)(dp.X - sp.X)) + 3 * Math.PI / 2) % (Math.PI * 2));

            //    //(float)Math.Acos(Vector3.Dot(Vector3.Backward, dir));
            //    var rect = laserline_animation.GetFrameBounds(e.duration, (float)Math.Abs(Vector3.Distance(sp, dp)*(1/e.size)*(1/zoomScale) ));
            //    spriteBatch.Draw(laserline_animation.texture, new Vector2(sp.X, sp.Y), rect, Color.White, e.rotation, 
            //                        new Vector2(rect.Width, 0) * 0.5f, laserline_animation.textureScale*e.size*zoomScale,
            //                        SpriteEffects.None, 0.0f);

            //}
            //spriteBatch.End();

            //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive);
            ////for_each (var e in laserlines)
            //for (int i = 0; i < missiles.Count; i++)
            //{
            //    var e = missiles[i];
            //    if (e.projectile.geometry.CollisionSkin == null || e.projectile.geometry.CollisionSkin.Collisions.Count==0)
            //    {
            //        //if (e.projectile.OwnValue.IsActive)
            //        //    continue;

            //        var zoomScale = 20 / Vector3.Distance(EyePosition, e.projectile.geometry.Position);

            //        var sp = GraphicsDevice.Viewport.Project(e.projectile.geometry.Position, projection, view, Matrix.Identity);
            //        var dp = GraphicsDevice.Viewport.Project(e.projectile.geometry.Position + e.projectile.geometry.Velocity, projection, view, Matrix.Identity);

            //        //var rect = laserline_animation.GetFrameBounds(e.duration, (float)Math.Abs(Vector3.Distance(sp, dp)));
            //        var rect = missile_description.texture.Bounds;
            //        spriteBatch.Draw(missile_description.texture, new Vector2(sp.X, sp.Y), rect, Color.White,
            //                            e.rotation, new Vector2(rect.Width, 0) * 0.5f, (1f / missile_description.scale) * zoomScale, SpriteEffects.None, 0.0f);
            //    }
            //}
            //spriteBatch.End();

            //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive);
            ////for_each (var e in laserlines)
            //for (int i = 0; i < nukes.Count; i++)
            //{
            //    var e = nukes[i];
            //    var zoomScale = 20 / Vector3.Distance(EyePosition, e.projectile.geometry.Position);
            //    //if (e.projectile.OwnValue.IsActive)
            //    //    continue;
            //    if (e.projectile.geometry.CollisionSkin == null || e.projectile.geometry.CollisionSkin.Collisions.Count == 0)
            //    {
            //        var sp = GraphicsDevice.Viewport.Project(e.projectile.geometry.Position, projection, view, Matrix.Identity);
            //        var dp = GraphicsDevice.Viewport.Project(e.projectile.geometry.Position + e.projectile.geometry.Velocity, projection, view, Matrix.Identity);

            //        //var rect = laserline_animation.GetFrameBounds(e.duration, (float)Math.Abs(Vector3.Distance(sp, dp)));
            //        var rect = nuke_description.texture.Bounds;
            //        spriteBatch.Draw(nuke_description.texture, new Vector2(sp.X, sp.Y), rect, Color.White,
            //                            e.rotation, new Vector2(rect.Width, 0) * 0.5f, (1f / nuke_description.scale)*zoomScale, SpriteEffects.None, 0.0f);
            //    }
            //}
            //spriteBatch.End();

            //spriteBatch.Begin();
            ////for_each (var v in vanishing_texts)
            //for (int i = 0; i < vanishing_texts.Count; i++)
            //{
            //    var v = vanishing_texts[i];
            //    var sp = GraphicsDevice.Viewport.Project(v.position, projection, view, Matrix.Identity);
            //    var alpha = v.time_left;
            //    var size = vanishing_text_font.MeasureString(v.text);
            //    spriteBatch.DrawString(vanishing_text_font, v.text, new Vector2(sp.X, sp.Y + 8 * v.time_left), Color.DarkBlue,
            //      0.0f, size * 0.5f, MathHelper.SmoothStep(0, 1, v.time_left), SpriteEffects.None, 0.0f);
            //    spriteBatch.DrawString(vanishing_text_font, v.text, new Vector2(sp.X + 1, sp.Y + 1 + 8 * v.time_left), Color.CornflowerBlue,
            //      0.0f, size * 0.5f, MathHelper.SmoothStep(0, 1, v.time_left), SpriteEffects.None, 0.0f);
            //}
            //spriteBatch.End();
            //spriteBatch.Begin();
            ////for_each (var l in Lasers)
            //for (int i = 0; i < Lasers.Count; i++)
            //{
            //    var l = Lasers[i];
            //    var sp = GraphicsDevice.Viewport.Project(l.Centroid, projection, view, Matrix.Identity);

            //    spriteBatch.Draw(LasersTexture, new Vector2(sp.X, sp.Y), null, Color.White, l.Angle + MathHelper.PiOver2,
            //                      new Vector2(LasersTexture.Width / 2, LasersTexture.Height / 2), l.Scale,
            //                      SpriteEffects.None, 0.0f);
            //}
            //spriteBatch.End();
            base.Draw(gameTime);
        }

        public void Reset()
        {
            explosions.Clear();
            shockwaves.Clear();
            //laserlines.Clear();
            //thunderbolts.Clear();
            //vanishing_texts.Clear();
            //Lasers.Clear();
            //plasma.Reset();
            //chainreaction.Reset();
            plasma_engine.Reset();
            //smoke.Reset();
            //warp.Reset();
            x.Reset();
            //machine_gun.Reset();
            //muzzle_flash.Reset();
            //missiles.Clear();
            //nukes.Clear(); 
        }
    }
}
